package com.xiaomaoguai.fcp.pre.kepler.router.rpc.server.servlet.handlers;

import com.xiaomaoguai.fcp.pre.kepler.router.rpc.constants.HttpConstants;
import com.xiaomaoguai.fcp.pre.kepler.router.rpc.server.servlet.response.NettyHttpServletResponse;
import com.xiaomaoguai.fcp.pre.kepler.router.rpc.server.servlet.response.SendResponse;
import com.xiaomaoguai.fcp.pre.kepler.router.rpc.server.servlet.core.NettyContext;
import com.xiaomaoguai.fcp.pre.kepler.router.rpc.server.servlet.core.NettyRequestDispatcher;
import com.xiaomaoguai.fcp.pre.kepler.router.rpc.server.servlet.request.NettyHttpServletRequest;

import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.timeout.ReadTimeoutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.async.WebAsyncUtils;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * 读入请求数据时，对请求URI获取分发器，找不到返回404错误. 找到则调用FilterChain进行业务逻辑，最后关闭输出流
 */
@ChannelHandler.Sharable
public class RequestDispatcherHandler extends ChannelInboundHandlerAdapter {

	private final static Logger log = LoggerFactory.getLogger(RequestDispatcherHandler.class);

	private final NettyContext context;

	RequestDispatcherHandler(NettyContext context) {
		this.context = checkNotNull(context);
	}

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object o) throws Exception {
		NettyHttpServletRequest servletRequest = new NettyHttpServletRequest(ctx, context, (FullHttpRequest) o);
		NettyHttpServletResponse servletResponse = SendResponse.crateResponse(servletRequest, HttpResponseStatus.OK,
				HttpUtil.isKeepAlive((HttpRequest) o));
		NettyRequestDispatcher dispatcher = (NettyRequestDispatcher) context
				.getRequestDispatcher(servletRequest.getRequestURI());
		if (dispatcher == null) {
			servletResponse.sendError(404);
			return;
		}
		dispatcher.dispatch(servletRequest, servletResponse);
		Object continuationFlag = servletRequest.getAttribute(HttpConstants.CONTINUATION);
		// 异步直接退出返回
		if ((continuationFlag != null && continuationFlag == Boolean.TRUE)
				|| WebAsyncUtils.getAsyncManager(servletRequest).isConcurrentHandlingStarted())
			return;
		SendResponse.resp(servletRequest.getCtx(), servletResponse);
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		if (cause instanceof ReadTimeoutException) {
			if (ctx.channel().isOpen()) {
				SendResponse.sendError(ctx, HttpResponseStatus.GATEWAY_TIMEOUT);
			}
			return;
		}
		log.error("Unexpected exception caught during request", cause);
		ctx.close();
	}

}
